import { Body, Injectable, Logger } from '@nestjs/common';
import { BaseService } from '@/core/base.service';
import { BaseQueryDto } from '@/dtos';
import { Prisma, Wish } from '@prisma/client';
import { WishOrderDto, WishTransaction2Dto } from '@/dtos/wish.dto';
import { compareHash, getCode, halt } from '@/utils';
import dayjs from 'dayjs';
import { SettingService } from '@/services/setting.service';
import { WishStatusEnum, WishTypeEnum } from '@/enums/wish.enum';
import {
  BillStatusEnum,
  BillTypeEnum,
  TokenLogTypeEnum,
  TokenStatusEnum,
} from '@/enums';
import { random } from 'radash';

@Injectable()
export class WishService extends BaseService {
  constructor(private readonly settingService: SettingService) {
    super();
  }

  async query({ params, sort }: BaseQueryDto<Prisma.WishWhereUniqueInput>) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    const sql = `
      SELECT #{fields} FROM Wish AS w
      LEFT JOIN Asset as a on a.id=w.assetId
      LEFT JOIN User as u on w.uid=u.id
      WHERE 1=1
        ${!!where.no ? `AND w.no = ${where.no}` : ''}
        ${!!where.assetId ? `AND a.id = ${where.assetId}` : ''}
        ${!!where.type ? `AND w.type = ${where.type}` : ''}
        ${where.status !== undefined ? `AND w.status = ${where.status}` : ''}
    `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(w.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<Wish[]>(
      `${sql.replace(
        '#{fields}',
        `
          w.id, w.no, a.title, a.image, w.type, w.price, w.payPrice, w.count, w.thirdNo, w.divideNo, w.status, 
          w.uid, u.mobile, u.realName, w.updatedAt, w.createdAt
        `,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => `w.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`)
              .join(',')
          : 'w.createdAt ASC'
      }
      LIMIT ${pageSize} 
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    // console.log(list);

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((item) => ({
        ...item,
        price: Number(item.price),
        payPrice: Number(item.payPrice),
      })),
    };
  }

  async list(
    { params, sort }: BaseQueryDto<Prisma.WishWhereUniqueInput>,
    userId,
  ) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    const sql = `
      SELECT #{fields} FROM Wish AS w
      LEFT JOIN Asset as a on a.id=w.assetId
      WHERE uid=${userId}
        ${!!where.no ? `AND w.no = ${where.no}` : ''}
        ${where.status !== undefined ? `AND w.status = ${where.status}` : ''}
    `;

    const [{ count }] = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace('#{fields}', 'COUNT(w.id) AS count'),
    );

    const list = await this.prisma.$queryRawUnsafe<Wish[]>(
      `${sql.replace(
        '#{fields}',
        `
          w.id, w.no, a.title, a.image, w.type, w.price, w.payPrice, w.count, w.thirdNo, w.divideNo, w.status, 
          w.uid, w.updatedAt, w.createdAt
        `,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => `w.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`)
              .join(',')
          : 'w.createdAt DESC'
      }
      LIMIT ${pageSize} 
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    const total = count ? Number(count) : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return {
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list.map((item) => ({
        ...item,
        price: Number(item.price),
        payPrice: Number(item.payPrice),
      })),
    };
  }

  async order({ price, count, assetId, pass }: WishOrderDto, uid) {
    const asset = await this.prisma.asset.findUnique({
      where: {
        id: assetId,
        isDeleted: false,
        isWish: true,
      },
    });

    if (!asset) {
      return halt('该资产未开放求购');
    }

    if (price > Number(asset.highestPrice)) {
      return halt('求购价不能超过最高限价');
    }

    return this.prisma.$transaction(async (tx) => {
      const user = await tx.user.findUnique({
        where: {
          id: uid,
          isBlockd: false,
          isDeleted: false,
        },
      });
      // Logger.debug(user);

      if (!user.txPass) {
        return halt('请先设置操作密码');
      }
      if (!(await compareHash(pass, user.txPass))) {
        return halt('密码错误');
      }

      if (Number(price * count) > Number(user.balance)) {
        return halt('余额不足');
      }

      await this.redisLock('wish_create', uid);

      const aOrderData = [];
      const aOrderNo = [];
      for (let i = 0; i < count; i++) {
        const tmpNo = await this.generateOrderNo();
        aOrderNo.push(tmpNo);

        aOrderData.push({
          uid,
          price,
          assetId,
          no: tmpNo,
          count: 1,
        });
      }

      if (aOrderData.length <= 0) {
        return halt('订单创建失败');
      }

      // const order = await tx.wish.create({
      //   data: {
      //     uid,
      //     price,
      //     assetId,
      //     no: await this.generateOrderNo(),
      //     count: 1,
      //     // fee: Number(price) * Number(setting.fee),
      //   },
      // });

      const orders = await tx.wish.createMany({
        data: aOrderData,
      });

      // Logger.debug(order);

      if (price * orders.count <= 0) {
        return halt('订单金额异常');
      }

      const newUser = await tx.user.update({
        where: {
          id: user.id,
          version: user.version,
        },
        data: {
          version: {
            increment: 1,
          },
          balance: {
            decrement: Number(price * orders.count),
          },
        },
      });

      await tx.bill.create({
        data: {
          no: await this.generateOrderNo(),
          total: Number(price * orders.count),
          fee: 0,
          type: BillTypeEnum.BUY,
          status: BillStatusEnum.COMPLETED,
          ownerId: user.id,
          remark:
            aOrderNo.length > 1
              ? `求购订单: ${aOrderNo[0]}等${orders.count}个订单`
              : `求购订单: ${aOrderNo[0]}`,
        },
      });

      // Logger.debug(newUser);

      await tx.wish.updateMany({
        where: {
          // id: order.id,
          no: {
            in: aOrderNo,
          },
        },
        data: {
          payPrice: Number(price),
          status: WishStatusEnum.PREPAID,
        },
      });
    });
  }

  async cancel(no, uid) {
    await this.redisLock('wish_cancel', uid, 2);
    const order = await this.prisma.wish.findUnique({
      where: {
        uid,
        no,
      },
    });

    if (!order) {
      return halt('订单不存在');
    }

    if (order.status != WishStatusEnum.PREPAID) {
      return halt('订单状态异常');
    }

    if (Number(order.payPrice) <= 0) {
      return halt('订单金额异常');
    }

    return this.prisma.$transaction(async (tx) => {
      const user = await tx.user.findUnique({
        where: {
          id: uid,
          isBlockd: false,
          isDeleted: false,
        },
      });

      if (!user) {
        return halt('用户异常');
      }

      const update = await tx.wish.update({
        where: {
          uid,
          no,
          status: WishStatusEnum.PREPAID,
        },
        data: {
          status: WishStatusEnum.CANCEL,
        },
      });

      if (update.status == WishStatusEnum.CANCEL) {
        const newUser = await tx.user.update({
          where: {
            id: uid,
            version: user.version,
          },
          data: {
            version: {
              increment: 1,
            },
            balance: {
              increment: Number(order.payPrice),
            },
          },
        });

        await tx.bill.create({
          data: {
            no: await this.generateOrderNo(),
            total: Number(order.payPrice),
            fee: 0,
            type: BillTypeEnum.REFUND,
            status: BillStatusEnum.COMPLETED,
            ownerId: user.id,
            remark: `取消求购，退款${order.payPrice}元`,
          },
        });
      }
    });
  }

  async transaction({ no, uid }: WishTransaction2Dto, tokenId = null) {
    const order = await this.prisma.wish.findUnique({
      where: {
        no,
      },
    });

    if (!order) {
      // return halt('订单不存在');
      throw new Error('订单不存在');
    }

    if (order.status != WishStatusEnum.PREPAID) {
      // return halt('订单状态异常');
      throw new Error('订单状态异常');
    }

    if (uid == order.uid) {
      throw new Error('转让与受让方不能为同一人');
    }

    const seller = await this.prisma.user.findUnique({
      where: {
        id: uid,
        isBlockd: false,
        isDeleted: false,
      },
    });

    if (!seller) {
      // return halt('用户异常');
      throw new Error('用户异常');
    }

    let type = WishTypeEnum.USER;
    if (tokenId == null) {
      const aToken = await this.prisma.token.findMany({
        select: {
          id: true,
        },
        where: {
          ownerId: seller.id,
          assetId: order.assetId,
          status: TokenStatusEnum.HOLD,
          isDeleted: false,
        },
      });

      if (aToken.length <= 0) {
        // return halt('交易失败，可交易数0');
        throw new Error('交易失败，可交易数0');
      }

      const tokenIdIndex = random(0, aToken.length - 1);

      tokenId = aToken[tokenIdIndex].id;
      type = WishTypeEnum.ADMIN;
    }

    Logger.debug(`交易TOKEN：${tokenId}`);
    // return false;

    const setting = await this.settingService.getDefault();
    return this.prisma.$transaction(async (tx) => {
      const token = await tx.token.update({
        where: {
          id: tokenId,
          status: TokenStatusEnum.HOLD,
          isDeleted: false,
        },
        data: {
          ownerId: order.uid,
        },
      });
      // Logger.debug(token);

      if (!token) {
        // return halt('交易失败，交易token异常');
        throw new Error('交易失败，交易token异常');
      }

      const update = await tx.wish.update({
        where: {
          id: order.id,
          status: WishStatusEnum.PREPAID,
        },
        data: {
          status: WishStatusEnum.COMPLETED,
          sellerId: seller.id,
          type: type,
          tokenId: token.id,
        },
      });

      if (!update) {
        // return halt('交易失败，订单更新失败');
        throw new Error('交易失败，订单更新失败');
      }

      const amount = Number(order.payPrice) * (1 - Number(setting.fee));
      await tx.user.update({
        where: {
          id: seller.id,
          version: seller.version,
        },
        data: {
          version: {
            increment: 1,
          },
          balance: {
            increment: amount,
          },
        },
      });

      try {
        await this.prisma.tokenLog.create({
          data: {
            log: `买入${order.payPrice}￥`,
            type: TokenLogTypeEnum.WISH,
            tokenId: tokenId,
            fromId: seller.id,
            recipientId: order.uid,
          },
        });
        const asset = await this.prisma.asset.findUnique({
          where: {
            id: token.assetId,
          },
          select: {
            title: true,
          },
        });
        await this.prisma.bill.create({
          data: {
            no: await this.generateOrderNo(),
            total: amount,
            fee: Number(order.payPrice) * Number(setting.fee),
            type: BillTypeEnum.SELL,
            status: BillStatusEnum.COMPLETED,
            ownerId: seller.id,
            remark: `求购卖出《${asset.title}》#${token.no}`,
          },
        });
      } catch (e) {
        // console.error(e);
      }
    });
  }

  async generateOrderNo() {
    return `${dayjs().format('YYYYMMDDHHmmss')}${await getCode(11)}`;
  }
}
